use qjs_sys as q;
use std::ffi::CString;

pub struct JSClass {
    class_id: u32,
    class_name: CString
}

impl JSClass {
    pub fn new(class_name: &str, runtime: *mut q::JSRuntime) -> Self {
        JSClass::new_full(class_name, runtime,None,None,None)
    }

    pub fn new_full(class_name: &str, runtime: *mut q::JSRuntime,
                    finalizer_fn:q::JSClassFinalizer,gc_mark_fn:q::JSClassGCMark,call_fn:q::JSClassCall) -> Self {
        unsafe {
            let mut class_id = 0u32;
            let class_name = CString::new(class_name).unwrap();
            q::JS_NewClassID(&mut class_id);
            let mut class_def = q::JSClassDef {
                class_name: class_name.as_ptr(),
                finalizer: finalizer_fn,
                gc_mark: gc_mark_fn,
                call: call_fn,
                exotic: std::ptr::null_mut(),
            };
            q::JS_NewClass(runtime, class_id, &mut class_def);
            JSClass {
                class_id,
                class_name,
            }
        }
    }

    pub fn class_name(&self) -> String {
        self.class_name.clone().into_string().unwrap()
    }

    pub fn class_id(&self) -> u32 {
        self.class_id
    }

    pub fn new_object(&self, ctx: *mut q::JSContext) -> JSClassOject {
        unsafe {
            let js_val = q::JS_NewObjectClass(ctx, self.class_id as i32);
            JSClassOject {
                value: js_val,
                class_id: self.class_id,
            }
        }
    }
}

pub struct JSClassOject {
    pub value: q::JSValue,
    class_id: u32,
}

impl JSClassOject {
    pub fn value(&self) -> q::JSValue {
        self.value
    }

    pub fn set_opaque<T>(&mut self, val: *mut T) {
        unsafe {
            q::JS_SetOpaque(self.value, std::mem::transmute(val));
        }
    }

    pub fn get_opaque<T>(&mut self) -> &mut T {
        unsafe {
            let ptr = q::JS_GetOpaque(self.value, self.class_id);
            std::mem::transmute(ptr)
        }
    }
}